﻿using Application.Common.Interfaces;
using AutoMapper;
using Common.Configurations;
using Common.Models;
using Domain.Repositories;
using Domain.Services;
using MediatR;
using Microsoft.Extensions.Options;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel;
using Application.Features.Auth.DTOs;
using Application.Features.Users.DTOs;
using System.Security.Claims;
using Common.Caching;
using Common.Extensions;
using Common.Utils;
using Microsoft.EntityFrameworkCore;
using System.Linq.Dynamic.Core;
using Common.Attributes;
using Common.Interfaces;

namespace Application.Features.Auth.Commands;

#nullable disable

/// <summary>
/// 通过用户名和密码登录
/// </summary>
[Description("密码登录")]
[GroupDescription("授权管理")]
public class LoginByPasswordCommand : IRequest<LoginOutput>
{
    /// <summary>
    /// 用户名
    /// </summary>
    [Required(ErrorMessage = "用户名是必填的")]
    public string UserName { get; set; }

    /// <summary>
    /// 密码
    /// </summary>
    [Required(ErrorMessage = "密码是必填的")]
    public string Password { get; set; }
}

/// <summary>
/// 处理程序
/// </summary>
public class LoginByPasswordCommandHandler : IRequestHandler<LoginByPasswordCommand, LoginOutput>
{
    private readonly UserDomainService _userDomainService;
    private readonly IUserRepository _userRepository;
    private readonly ITokenService _tokenService;
    private readonly IOptions<JwtSettings> _optJwtSettings;
    private readonly IMapper _mapper;
    private readonly IMenuRepository _menuRepository;
    private readonly ICacheService _cacheService;
    private readonly IApplicationDbContext _dbContext;

    public LoginByPasswordCommandHandler(
        UserDomainService userDomainService,
        ITokenService tokenService,
        IOptions<JwtSettings> optJwtSettings,
        IMapper mapper,
        IMenuRepository menuRepository,
        ICacheService cacheService,
        IApplicationDbContext dbContext,
        IUserRepository userRepository)
    {
        _userDomainService = userDomainService;
        _tokenService = tokenService;
        _optJwtSettings = optJwtSettings;
        _mapper = mapper;
        _menuRepository = menuRepository;
        _cacheService = cacheService;
        _dbContext = dbContext;
        _userRepository = userRepository;
    }

    /// <summary>
    /// 业务逻辑
    /// </summary>
    /// <param name="request">请求参数</param>
    /// <param name="cancellationToken">取消标记</param>
    /// <returns>返回Token</returns>
    public async Task<LoginOutput> Handle(LoginByPasswordCommand request, CancellationToken cancellationToken)
    {
        var user = await _userDomainService.LoginByPasswordAsync(request.UserName, request.Password);

        if (user == null) throw new InvalidOperationException($"登录失败，用户名或密码错误");
        if (!user.IsActive) throw new InvalidOperationException($"登录失败，账号被停用或未激活");
        if (user.LockoutEnabled && user.LockoutEnd > DateTime.Now) throw new InvalidOperationException($"账号已被锁，于{user.LockoutEnd}解锁");

        user.AccessFailedCount = 0;
        if (user.LockoutEnabled) 
        {
            user.IsUnLock();
            await _userRepository.UpdateAsync(user);
        }
        if (user.AccessFailedCount > 0) 
        {
            await _userRepository.UpdateAsync(user);
        }

        //var userRoles = await _dbContext.Roles
        //   .Include(r => r.UserRoles)
        //   .Where(x => x.UserRoles.Any(ur => ur.UserId == user.Id))
        //   .ToListAsync();

        var userMenus = await _menuRepository.GetUserMenusByUserIdAsync(user.Id, user.IsSystemAccount ?? false);
        var allowAccessPaths = userMenus.Where(x => x.Meta.Hidden == false).Select(menu => (menu.HttpMethods + menu.Url)).ToList();
        List<Claim> claims = new()
        {
            new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
            new Claim(ClaimTypes.Name, user.UserName!),
            new Claim(ApplicationClaimTypes.IsSystemAccount, user.IsSystemAccount?.ToString()??"False"),
            new Claim(ApplicationClaimTypes.ProfilePictureDataUrl, user.ProfilePictureDataUrl ?? ""),
            new Claim(ApplicationClaimTypes.TenantName, user.TenantName ?? ""),
            new Claim(ApplicationClaimTypes.TenantId, user.TenantId ?? "")
        };

        string accessToken = _tokenService.Build(claims, _optJwtSettings.Value);
        string refreshToken = _tokenService.Build(claims, _optJwtSettings.Value, true);

        var data = new LoginOutput
        {
            Username = request.UserName,
            UserInfo = _mapper.Map<UserOutput>(user),
           // Roles = userRoles?.Select(s=>s.RoleCode).ToList(),
            AccessToken = accessToken,
            RefreshToken = refreshToken,
            Expires = DateTime.Now.AddSeconds(_optJwtSettings.Value.ExpireSeconds).ToString("yyyy/MM/dd HH:mm:ss"),
        };

        _cacheService.Set(
            user.Id.ToString().ToAuthUserCaCheKey(),
            data.ToJson(),
            TimeSpan.FromSeconds(_optJwtSettings.Value.ExpireSeconds));

        _cacheService.Set(
            user.Id.ToString().ToAuthUserPathsCaCheKey(),
           allowAccessPaths,
            TimeSpan.FromSeconds(_optJwtSettings.Value.ExpireSeconds));

        return data;
    }
}
